package main

import (
	"flag"
	"fmt"
	"io"
	"net"
	"os"
)

type Client struct {
	ServerIp   string
	ServerPort int
	Name       string
	conn       net.Conn
	flag       int
}

func NewClient(serverIp string, serverPort int) *Client {
	// 创建客户端对象,
	client := &Client{
		ServerIp:   serverIp,
		ServerPort: serverPort,
		flag:       999,
	}

	// 连接 server,
	conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", serverIp, serverPort))
	if err != nil {
		fmt.Println("net.Dial() error:", err)
		return nil
	}
	client.conn = conn

	// 返回 client 对象,
	return client
}

// 处理 server 回应的消息, 直接显示到标准输出即可,
func (client *Client) DealResponse() {
	// 一旦  client.conn 有数据, 就直接拷贝到 os.Stdout, 永久阻塞监听,
	io.Copy(os.Stdout, client.conn)
	// 等价于下面的 ==>
	// for {
	// 	buf := make([]byte, 256)
	// 	client.conn.Read(buf)
	// 	fmt.Println(buf)
	// }
}

func (client *Client) menu() bool {
	var flag int
	fmt.Println("1.群聊模式\n" + "2.私聊模式\n" + "3.更新用户名\n" + "4.查询所有在线用户\n" + "5.查询当前用户\n" + "0.退出\n")
	fmt.Scanln(&flag)
	if flag >= 0 && flag <= 5 {
		client.flag = flag
		return true
	} else {
		fmt.Println(">>>>> please input 0--4")
		return false
	}
}

func (client *Client) Run() {
	for client.flag != 0 {
		for client.menu() != true {

		}

		// 根据 client.flag 处理不同的业务,
		switch client.flag {
		case 1:
			// 群聊模式,
			// fmt.Println("群聊模式...")
			client.PublicChat()
			break
		case 2:
			// 私聊模式,
			// fmt.Println("私聊模式...")
			client.PrivateChat()
			break
		case 3:
			// 更新用户名,
			// fmt.Println("更新用户名...")
			client.UpdateName()
			break
		case 4:
			// 查询所有在线用户
			client.QueryUsers()
		case 5:
			// 查询当前用户,
			// fmt.Println("查询当前用户...")
			client.WhoAmI()
			break
		}
	}
}

func (client *Client) PublicChat() {
	var chatMsg string
	fmt.Println(">>>>>> please input chat msg or exit.")
	fmt.Scanln(&chatMsg)
	for chatMsg != "exit" {
		// 发送给服务器,
		// 消息不为空则发送,
		if len(chatMsg) != 0 {
			sendMsg := chatMsg + "\n"
			_, err := client.conn.Write([]byte(sendMsg))
			if err != nil {
				fmt.Println("conn.Write err:", err)
				break
			}
		}

		chatMsg = ""
		fmt.Println(">>>>>> please input chat msg or exit.")
		fmt.Scanln(&chatMsg)
	}
}

// 查询在线用户,
func (client *Client) QueryUsers() {
	sendMsg := "who\n"
	_, err := client.conn.Write([]byte(sendMsg))
	if err != nil {
		fmt.Println("conn.Write err:", err)
		return
	}
}

// 私聊模式,
func (client *Client) PrivateChat() {
	// 查询当前都有哪些用户在线,
	var remoteName string
	client.QueryUsers()
	// 提示用户选择一个用户进入私聊,
	fmt.Println(">>>>>> please input remoteName or exit.")
	fmt.Scanln(&remoteName)
	for remoteName != "exit" {
		// 输入聊天内容,
		var chatMsg string
		fmt.Println(">>>>>> please input chat msg or exit.")
		fmt.Scanln(&chatMsg)
		for chatMsg != "exit" {
			// 消息不为空则发送,
			if len(chatMsg) != 0 {
				sendMsg := "to|" + remoteName + "|" + chatMsg + "\n\n"
				_, err := client.conn.Write([]byte(sendMsg))
				if err != nil {
					fmt.Println("conn.Write err:", err)
					break
				}
			}

			chatMsg = ""
			fmt.Println(">>>>>> please input chat msg or exit.")
			fmt.Scanln(&chatMsg)
		}

		// 再次选择一个新的用户进行私聊,
		client.QueryUsers()
		fmt.Println(">>>>>> please input remoteName or exit.")
		fmt.Scanln(&remoteName)

	}

}

func (client *Client) UpdateName() bool {
	fmt.Println(">>>>>> please input username:")
	fmt.Scanln(&client.Name)
	sendMsg := "rename|" + client.Name + "\n"
	_, err := client.conn.Write([]byte(sendMsg))
	if err != nil {
		fmt.Println("conn.Write err:", err)
		return false
	}
	return true
}

func (client *Client) WhoAmI() {
	_, err := client.conn.Write([]byte("whoami"))
	if err != nil {
		fmt.Println("conn.Write err:", err)
	}
}

// ./client -ip 127.0.0.1 -port 8888
var serverIp string
var serverPort int

func init() {
	flag.StringVar(&serverIp, "ip", "127.0.0.1", "serverIp(default 127.0.0.1)")
	flag.IntVar(&serverPort, "port", 8888, "serverPort(default 8888)")
}

func main() {
	// 命令行解析,
	flag.Parse()

	client := NewClient(serverIp, serverPort)
	if client == nil {
		fmt.Printf(">>>> connect server failed!\n")
		return
	}
	fmt.Printf(">>>> connect server success!\n")

	// 当 client 连接成功后, 就开启一个 goroutine 去处理 server 发来的消息,
	go client.DealResponse()

	// 启动客户端的业务,
	client.Run()

}
